package bank.server.sockets;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

import bank.Account;
import bank.InactiveException;
import bank.OverdrawException;
import bank.driver.local.LocalAccount;
import bank.driver.local.LocalBank;
import bank.server.sockets.account.*;
import bank.server.sockets.bank.*;

public class BankHandler implements Runnable {
	
	private Socket socket;
	private LocalBank bank;

	public BankHandler(Socket s, LocalBank bank) {
		this.socket = s;
		this.bank = bank;
	}

	@Override
	public void run() {
		while(true) {
			try {
				ObjectInputStream in = new ObjectInputStream(
					this.socket.getInputStream()
				);
				ObjectOutputStream out = new ObjectOutputStream(
					this.socket.getOutputStream()
				);
				while(true) {
					Request request = (Request) in.readObject();
					Response response = handle(request);
					out.writeObject(response);
					out.flush();
				}
			} catch (Exception e) {
				// Ignore
			}
		}
	}

	private Response handle(Request request) throws IOException {
		if (request instanceof AccountRequest) {
			return handle((AccountRequest)request);
		}
		else if (request instanceof BankRequest) {
			return handle((BankRequest)request);
		}
		return null;
	}
	
	private BankResponse handle(BankRequest request) throws IOException {
		if (request instanceof CreateAccountRequest) {
			return new CreateAccountResponse(
				this.bank.createAccount(
					((CreateAccountRequest) request).getOwner()
				)
			);
		}
		else if (request instanceof GetAccountNumbersRequest) {
			return new GetAccountNumbersResponse(
				this.bank.getAccountNumbers()
			);
		}
		else if (request instanceof GetAccountRequest) {
			Account a = this.bank.getAccount(
				((GetAccountRequest) request).getNumber()
			);
			return new GetAccountResponse(
				a.getOwner(), a.getNumber(), a.getBalance(), a.isActive()
			);
		}
		else if (request instanceof RemoveAccountRequest) {
			return new RemoveAccountResponse(
				this.bank.removeAccount(
					((RemoveAccountRequest) request).getNumber()
				)
			);
		}
		else if (request instanceof TransferRequest) {
			Exception ex = null;
			try {
				this.bank.transfer(
					this.bank.getAccount(
						((TransferRequest) request).getAccountNumberA()
					),
					this.bank.getAccount(
						((TransferRequest) request).getAccountNumberB()
					),
					((TransferRequest) request).getAmount()
				);
				return new TransferResponse(null);
			} catch (IllegalArgumentException e) {
				ex = e;
			} catch (InactiveException e) {
				ex = e;
			} catch (OverdrawException e) {
				ex = e;
			}
			return new TransferResponse(ex);
		}
		else {
			return null;
		}
	}
	
	private AccountResponse handle(AccountRequest request) throws IOException {
		LocalAccount a = this.bank.getAccount(request.getNumber());
		if (request instanceof DeactivateRequest) {
			a.deactivate();
			return new DeactivateResponse();
		}
		else if (request instanceof DepositRequest) {
			Exception ex = null;
			try {
				a.deposit(((DepositRequest) request).getAmount());
			} catch (IllegalArgumentException e) {
				ex = e;
			} catch (InactiveException e) {
				ex = e;
			}
			return new DepositResponse(ex);
		}
		else if (request instanceof GetBalanceRequest) {
			return new GetBalanceResponse(a.getBalance());
		}
		else if (request instanceof IsActiveRequest) {
			return new IsActiveResponse(a.isActive());
		}
		else if (request instanceof WithdrawRequest) {
			Exception ex = null;
			try {
				a.withdraw(((WithdrawRequest) request).getAmount());
			} catch (OverdrawException e) {
				ex = e;
			} catch (IllegalArgumentException e) {
				ex = e;
			} catch (InactiveException e) {
				ex = e;
			}
			return new WithdrawResponse(ex);
		}
		else {
			return null;
		}
	}

}
